/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package stud.cp.user.dbaccess;

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import org.apache.log4j.Logger;
import stud.cp.core.dbaccess.DAOException;
import stud.cp.user.valueobjects.History;
import stud.cp.user.valueobjects.Property;

/**
 * Класс, описывающий
 * доступ к базе данных
 * с помощью пользовательских запросов.
 * @author Morfant
 */
public class UserQueriesDAO {

    /**
     * Логгер.
     */
    private static Logger logger = Logger.getLogger("CPTNP");
    /**
     * Список названий колонок таблицы history..
     */
    private static String[] historyColumnName = {"history_id",
        "weapon_id",
        "property_id",
        "user_id",
        "change_date",
        "new_value",
        "old_value"};
    /**
     * Список названий колонок таблицы properties..
     */
    private static String[] propetyColumnName = {"property_id",
        "property_type_id",
        "property_group_id",
        "property_name",//[4]
        "property_type_def_id"};
    /**
     * Имя колонок таблицы weapon
     */
    private static String[] weaponColumnName = {"weapon_id", "weapon_name", "description", "weapon_type_id"};//[1]
    /**
     * Список названий колонок таблицы users..
     */
    private static String[] usersColumnName = {"user_id",
        "user_type_id",
        "user_name",//[2]
        "password"};
    /**
     * Имя колонок таблицы property_types
     */
    private static String[] propertyTypeColumnName = {"property_type_id", "type_name"};
    /**
     * Имя колонок таблицы propertyGroups
     */
    private static String[] propertyGroupsColumnName = {"property_group_id", "group_name"};
    /**
     * Имя колонок таблицы user_types
     */
    private static String[] userTypesColumnName = {"user_type_id", "name"};
    /**
     * Имена специальных колонок.
     */
    private static String[] specialColumnName = {"user_count", "weapon_count"};
    /**
     * Список названий колонок таблицы weapon_types.
     */
    private static String[] weaponTypesColumnName = {"weapon_type_id", "name", "description"};
    /**
     * Коннект к базе данных.
     */
    private Connection connection = null;

    /**
     * Конструктор, который устанавливает
     * коннект к базе данных.
     * @param connection Связь с базой данных.
     */
    public UserQueriesDAO(Connection connection) {
        this.connection = connection;
    }

    /**
     * Запрос, для получения
     * количества пользователей каждого типа.
     * @return Запрос.
     */
    private String getQueryForGetUserCountByType() {
        StringBuilder query = new StringBuilder();
        query.append("select ut.user_type_id,ut.name,count(u.user_id) as \"user_count\" \n");
        query.append("from user_types ut \n");
        query.append("inner join users u on ut.user_type_id=u.user_type_id \n");
        query.append("group by ut.user_type_id,ut.name \n");
        return query.toString();
    }

    /**
     * Метод, возращающий
     * карту состояющую из типов пользователей и
     * их количества.
     * @return Карта из типов пользователей и их количества.
     */
    public Map<String, Integer> getUserCountByType() throws DAOException {
        Map<String, Integer> mapOfData = new HashMap<String, Integer>();
        ResultSet set = null;
        PreparedStatement prestat = null;
        try {
            prestat = connection.prepareStatement(getQueryForGetUserCountByType());
            set = prestat.executeQuery();
            while (set.next()) {
                mapOfData.put(set.getString(userTypesColumnName[1]), set.getInt(specialColumnName[0]));
            }
        } catch (SQLException ex) {
            logger.debug("DAO_propety_group", ex);
            throw new DAOException("Ошибка получения данных.", ex);
        } finally {
            logger.debug("SQL:\n" + getQueryForGetUserCountByType());
            if (prestat != null) {
                try {
                    prestat.close();
                } catch (SQLException ex) {
                    logger.debug("DAO_propety", ex);
                    throw new DAOException("Ошибка закрытия PrepareStatement`а.", ex);
                }
            }
        }
        return mapOfData;
    }

    /**
     * Запрос, позволяющий при выполнении
     * получить полностью историю для конкретного оружия.
     * @return Строка запроса.
     */
    private String getQueryForAllHistoryForUser(int user_id) {
        StringBuilder query = new StringBuilder();
        query.append("select h.history_id,h.weapon_id,h.property_id,h.user_id,h.change_date,h.new_value,h.old_value,");
        query.append("w.name as \"weapon_name\",p.name as \"property_name\",u.name as \"user_name\" \n");
        query.append("from history h \n");
        query.append("inner join weapons w on w.weapon_id=h.weapon_id \n");
        query.append("inner join properties p on p.property_id=h.property_id \n");
        query.append("inner join users u on u.user_id=h.user_id \n");
        query.append("where u.user_id=");
        query.append(user_id);
        query.append("\n order by h.history_id");
        return query.toString();
    }

    /**
     * Метод, который возвращает
     * список историю для конкретного пользователя.
     * @return Список данных.
     */
    public List<History> getAllHistoryForUser(int user_id) throws DAOException {
        List<History> data_list = new LinkedList<History>();
        ResultSet set = null;
        PreparedStatement prestat = null;
        try {
            prestat = connection.prepareStatement(getQueryForAllHistoryForUser(user_id));
            set = prestat.executeQuery();
            while (set.next()) {
                data_list.add(new History(set.getInt(historyColumnName[0]),
                        set.getInt(historyColumnName[1]),
                        set.getInt(historyColumnName[2]),
                        set.getInt(historyColumnName[3]),
                        set.getDate(historyColumnName[4]),
                        set.getString(historyColumnName[5]),
                        set.getString(historyColumnName[6]),
                        set.getString(propetyColumnName[3]),
                        set.getString(usersColumnName[2]),
                        set.getString(weaponColumnName[1])));
            }
        } catch (SQLException ex) {
            logger.debug("DAO_propeties", ex);
            throw new DAOException("Ошибка получения списка данных.", ex);
        } finally {
            if (prestat != null) {
                try {
                    prestat.close();
                } catch (SQLException ex) {
                    logger.debug("DAO_propety", ex);
                    throw new DAOException("Ошибка закрытия ResultSet`а.", ex);
                }
            }
        }
        return data_list;
    }

    /**
     * Запрос, позволяющий при выполнении
     * получить полностью историю для конкретного оружия.
     * @return Строка запроса.
     */
    private String getQueryForAllHistoryForDate(String date) {
        StringBuilder query = new StringBuilder();
        query.append("select h.history_id,h.weapon_id,h.property_id,h.user_id,h.change_date,h.new_value,h.old_value,");
        query.append("w.name as \"weapon_name\",p.name as \"property_name\",u.name as \"user_name\" \n");
        query.append("from history h \n");
        query.append("inner join weapons w on w.weapon_id=h.weapon_id \n");
        query.append("inner join properties p on p.property_id=h.property_id \n");
        query.append("inner join users u on u.user_id=h.user_id \n");
        query.append("where h.change_date='");
        query.append(date);
        query.append("'\n order by h.history_id");
        return query.toString();
    }

    /**
     * Метод, который возвращает
     * список историю для конкретной даты
     * @return Список данных.
     */
    public List<History> getAllHistoryForDate(Date date) throws DAOException {
        List<History> data_list = new LinkedList<History>();
        ResultSet set = null;
        PreparedStatement prestat = null;
        try {
            DateFormat formatter = new SimpleDateFormat("dd-MMM-yyyy");
            prestat = connection.prepareStatement(getQueryForAllHistoryForDate(formatter.format(date)));
            set = prestat.executeQuery();
            while (set.next()) {
                data_list.add(new History(set.getInt(historyColumnName[0]),
                        set.getInt(historyColumnName[1]),
                        set.getInt(historyColumnName[2]),
                        set.getInt(historyColumnName[3]),
                        set.getDate(historyColumnName[4]),
                        set.getString(historyColumnName[5]),
                        set.getString(historyColumnName[6]),
                        set.getString(propetyColumnName[3]),
                        set.getString(usersColumnName[2]),
                        set.getString(weaponColumnName[1])));
            }
        } catch (SQLException ex) {
            logger.debug("DAO_propeties", ex);
            throw new DAOException("Ошибка получения списка данных.", ex);
        } finally {
            if (prestat != null) {
                try {
                    prestat.close();
                } catch (SQLException ex) {
                    logger.debug("DAO_propety", ex);
                    throw new DAOException("Ошибка закрытия ResultSet`а.", ex);
                }
            }
        }
        return data_list;
    }

    /**
     * Запрос, позволяющий при выполнении
     * получить список свойств принадлежащие
     * определенной группе свойств
     * @param property_group_id ID типа оружия.
     * @return Строка запроса.
     */
    private String getQueryForAllPropertyForPropertyGroup(int property_group_id) {
        StringBuilder query = new StringBuilder();
        query.append("select p.property_id,p.property_type_id, p.property_group_id,p.name as \"property_name\",p.property_type_def_id");
        query.append(",pt.name as \"type_name\",pg.name as \"group_name\" \n");
        query.append("from properties  p \n");
        query.append("inner join property_types pt on p.property_type_id=pt.property_type_id \n");
        query.append("inner join property_groups pg on p.property_group_id=pg.property_group_id \n");
        //       query.append("inner join weapon_types_properties wtp on p.property_id=wtp.property_id \n");
        query.append("where pg.property_group_id=");
        query.append(property_group_id);
        query.append("\n order by pg.name");
        return query.toString();
    }

    /**
     * Запрос, который позволяет получить
     * все свойства для определенной группы свойств.
     * @param properti_group_id ID типа оружия.
     * @return Список свойств.
     * @throws DAOException Ошибка доступа к данным.
     */
    public List<Property> getAllPropertyForPropertyGroup(int properti_group_id) throws DAOException {
        List<Property> data_list = new LinkedList<Property>();
        ResultSet set = null;
        PreparedStatement prestat = null;
        try {
            prestat =
                    connection.prepareStatement(
                    getQueryForAllPropertyForPropertyGroup(properti_group_id));
            set = prestat.executeQuery();
            while (set.next()) {
                data_list.add(new Property(set.getInt(propetyColumnName[0]),
                        set.getInt(propetyColumnName[1]),
                        set.getInt(propetyColumnName[2]),
                        set.getString(propetyColumnName[3]),
                        set.getInt(propetyColumnName[4]),
                        set.getString(propertyTypeColumnName[1]),
                        set.getString(propertyGroupsColumnName[1])));
            }
        } catch (SQLException ex) {
            logger.debug("DAO_propeties", ex);
            throw new DAOException("Ошибка получения списка данных для определенного типа оружия.", ex);
        } finally {
            if (prestat != null) {
                try {
                    prestat.close();
                } catch (SQLException ex) {
                    logger.debug("DAO_propety", ex);
                    throw new DAOException("Ошибка закрытия ResultSet`а.", ex);
                }
            }
        }
        return data_list;
    }

    /**
     * Запрос, для получения
     * количества оружия по типам для заданного свойства
     * @param property_id ID свойства.
     * @return Запрос.
     */
    private String getQueryForWeaponCountAndTypeForProperty(int property_id) {
        StringBuilder query = new StringBuilder();
        query.append("select wt.weapon_type_id,wt.name,count(distinct w.weapon_id) as \"weapon_count\" \n");
        query.append("from properties p \n");
        query.append("inner join weapon_types_properties wtp on wtp.property_id=p.property_id \n");
        query.append("inner join weapons w on w.weapon_type_id=wtp.weapon_type_id \n");
        query.append("inner join weapon_types wt on wt.weapon_type_id=wtp.weapon_type_id \n");
        query.append("where p.property_id=");
        query.append(property_id);
        query.append("\n group by wt.weapon_type_id,wt.name \n");
        return query.toString();
    }

    /**
     * Метод, возращающий
     * карту состояющую из названия типа оружия и количества его экземпляров.
     * для определенного свойства.
     * @param ID свойства.
     * @return Карта из типов пользователей и их количества.
     */
    public Map<Integer, List<String>> getWeaponCountAndTypeForProperty(int property_id) throws DAOException {
        Map<Integer, List<String>> mapOfData = new HashMap<Integer, List<String>>();
        ResultSet set = null;
        PreparedStatement prestat = null;
        List<String> list = null;
        try {
            prestat = connection.prepareStatement(getQueryForWeaponCountAndTypeForProperty(property_id));
            set = prestat.executeQuery();
            while (set.next()) {
                list = new LinkedList<String>();
                list.add(set.getString(weaponTypesColumnName[1]));
                list.add("" + set.getInt(specialColumnName[1]));
                mapOfData.put(set.getInt(weaponTypesColumnName[0]), list);
                //   mapOfData.put(set.getString(weaponTypesColumnName[1]), set.getInt(specialColumnName[1]));
            }
        } catch (SQLException ex) {
            logger.debug("DAO_propety_group", ex);
            throw new DAOException("Ошибка получения данных.", ex);
        } finally {
            logger.debug("SQL:\n" + getQueryForGetUserCountByType());
            if (prestat != null) {
                try {
                    prestat.close();
                } catch (SQLException ex) {
                    logger.debug("DAO_propety", ex);
                    throw new DAOException("Ошибка закрытия PrepareStatement`а.", ex);
                }
            }
        }
        return mapOfData;
    }

    /**
     * Запрос, для получения
     * типов оружия, содержащих заданные свойства.
     * @param listOfProperties  Список ID свойств.
     * @return Запрос.
     */
    private String getQueryForWeaponTypeForProperties(List<Integer> listOfProperties) {
        StringBuilder query = new StringBuilder();
        query.append("select wt.weapon_type_id,wt.name \n");
        query.append("from weapon_types wt \n");
        query.append("inner join weapon_types_properties wtp on wt.weapon_type_id=wtp.weapon_type_id \n");
        query.append("where wtp.property_id in (");
        int size = listOfProperties.size();
        for (int i = 0; i < size; i++) {
            query.append(listOfProperties.get(i));
            if (i < size - 1) {
                query.append(",");
            }
        }
        query.append(")\n group by wt.weapon_type_id,wt.name \n");
        query.append("having count(wtp.property_id)=\n");
        query.append(size);
        return query.toString();
    }

    /**
     * Метод, возращающий
     * карту состояющую из названия типа оружия и количества его экземпляров.
     * для определенного свойства.
     * @param listOfProperties ID свойств.
     * @return Карта из типов пользователей и их количества.
     */
    public List<String> getWeaponTypeForProperties(List<Integer> listOfProperties) throws DAOException {
        List<String> listOfData = new LinkedList<String>();
        ResultSet set = null;
        PreparedStatement prestat = null;
        try {
            prestat = connection.prepareStatement(getQueryForWeaponTypeForProperties(listOfProperties));
            set = prestat.executeQuery();
            while (set.next()) {
                listOfData.add(set.getString(weaponTypesColumnName[1]));
            }
        } catch (SQLException ex) {
            logger.debug("DAO_propety_group", ex);
            throw new DAOException("Ошибка получения данных.", ex);
        } finally {
            logger.debug("SQL:\n" + getQueryForGetUserCountByType());
            if (prestat != null) {
                try {
                    prestat.close();
                } catch (SQLException ex) {
                    logger.debug("DAO_propety", ex);
                    throw new DAOException("Ошибка закрытия PrepareStatement`а.", ex);
                }
            }
        }
        return listOfData;
    }

    /**
     * Запрос, позволяющий при выполнении
     * получить список свойств принадлежащие
     * определенной группе свойств
     * @param property_group_id ID типа оружия.
     * @return Строка запроса. 
     */
    private String getQueryForAllPropertyForIDs(List<Integer> listOfIDs) {
        StringBuilder query = new StringBuilder();
        query.append("select distinct p.property_id,p.property_type_id, p.property_group_id,p.name as \"property_name\",p.property_type_def_id");
        query.append(",pt.name as \"type_name\",pg.name as \"group_name\" \n");
        query.append("from properties  p \n");
        query.append("inner join property_types pt on p.property_type_id=pt.property_type_id \n");
        query.append("inner join property_groups pg on p.property_group_id=pg.property_group_id \n");
 //       query.append("inner join weapon_types_properties wtp on p.property_id=wtp.property_id \n");
        query.append("where p.property_id in (");
        int size = listOfIDs.size();
        for (int i = 0; i < size; i++) {
            query.append(listOfIDs.get(i));
            if (i < size - 1) {
                query.append(",");
            }
        }
        query.append(") \n order by pg.name");
        return query.toString();
    }

    /**
     * Запрос, который позволяет получить
     * своства по заданным ID.
     * @param listOfIDs ID свойств.
     * @return Список свойств.
     * @throws DAOException Ошибка доступа к данным.
     */
    public List<Property> getAllPropertyForIDs(List<Integer> listOfIDs) throws DAOException {
        List<Property> data_list = new LinkedList<Property>();
        ResultSet set = null;
        PreparedStatement prestat = null;
        try {
            prestat =
                    connection.prepareStatement(
                    getQueryForAllPropertyForIDs(listOfIDs));
            set = prestat.executeQuery();
            while (set.next()) {
                data_list.add(new Property(set.getInt(propetyColumnName[0]),
                        set.getInt(propetyColumnName[1]),
                        set.getInt(propetyColumnName[2]),
                        set.getString(propetyColumnName[3]),
                        set.getInt(propetyColumnName[4]),
                        set.getString(propertyTypeColumnName[1]),
                        set.getString(propertyGroupsColumnName[1])));
            }
        } catch (SQLException ex) {
            logger.debug("DAO_propeties", ex);
            throw new DAOException("Ошибка получения списка данных для определенного типа оружия.", ex);
        } finally {
            if (prestat != null) {
                try {
                    prestat.close();
                } catch (SQLException ex) {
                    logger.debug("DAO_propety", ex);
                    throw new DAOException("Ошибка закрытия ResultSet`а.", ex);
                }
            }
        }
        return data_list;
    }

    /**
     * Запрос, для получения
     * количества оружия по типам для заданной группы
     * @param property_group_id ID группы.
     * @return Запрос.
     */
    private String getQueryForWeaponCountAndTypeForGroup(int property_group_id) {
        StringBuilder query = new StringBuilder();
        query.append("select wt.weapon_type_id,wt.name,count(distinct w.weapon_id) as \"weapon_count\" \n");
        query.append("from properties p \n");
        query.append("inner join weapon_types_properties wtp on wtp.property_id=p.property_id \n");
        query.append("inner join weapons w on w.weapon_type_id=wtp.weapon_type_id \n");
        query.append("inner join weapon_types wt on wt.weapon_type_id=wtp.weapon_type_id \n");
        query.append("inner join property_groups pg on pg.property_group_id=p.property_group_id \n");
        query.append("where pg.property_group_id=");
        query.append(property_group_id);
        query.append("\n group by wt.weapon_type_id,wt.name \n");
        return query.toString();
    }

    /**
     * Метод, возращающий
     * карту состояющую из названия типа оружия и количества его экземпляров.
     * для определенного свойства.
     * @param ID свойства.
     * @return Карта из типов пользователей и их количества.
     */
    public Map<Integer, List<String>> getWeaponCountAndTypeForGroup(int property_group_id) throws DAOException {
        Map<Integer, List<String>> mapOfData = new HashMap<Integer, List<String>>();
        ResultSet set = null;
        PreparedStatement prestat = null;
        List<String> list = null;
        try {
            prestat = connection.prepareStatement(getQueryForWeaponCountAndTypeForGroup(property_group_id));
            set = prestat.executeQuery();
            while (set.next()) {
                list = new LinkedList<String>();
                list.add(set.getString(weaponTypesColumnName[1]));
                list.add("" + set.getInt(specialColumnName[1]));
                mapOfData.put(set.getInt(weaponTypesColumnName[0]), list);
                //   mapOfData.put(set.getString(weaponTypesColumnName[1]), set.getInt(specialColumnName[1]));
            }
        } catch (SQLException ex) {
            logger.debug("DAO_propety_group", ex);
            throw new DAOException("Ошибка получения данных.", ex);
        } finally {
            logger.debug("SQL:\n" + getQueryForGetUserCountByType());
            if (prestat != null) {
                try {
                    prestat.close();
                } catch (SQLException ex) {
                    logger.debug("DAO_propety", ex);
                    throw new DAOException("Ошибка закрытия PrepareStatement`а.", ex);
                }
            }
        }
        return mapOfData;
    }
//
//    /**
//     * Запрос, позволяющий при выполнении
//     * получить все свойства, включенные в объектные типы.
//     * @return Строка запроса.
//     */
//    private String getQueryForAllPropertyIncludeInWeaponType() {
//        StringBuilder query = new StringBuilder();
//        query.append("select p.property_id,p.property_type_id, p.property_group_id,p.name,p.property_type_def_id");
//        query.append(",pt.name as \"type_name\",pg.name as \"group_name\" \n");
//        query.append("from properties  p \n");
//        query.append("inner join property_types pt on p.property_type_id=pt.property_type_id \n");
//        query.append("inner join property_groups pg on p.property_group_id=pg.property_group_id \n");
//        query.append("inner join weapon_types_properties wtp on p.property_id=wtp.property_id \n");
//        return query.toString();
//    }
//
//    /**
//     * Метод, который возвращает
//     * список свойств, включенных в типы оружия.
//     * @return Список данных.
//     */
//    public List<Property> getAllPropertyIncludeInWeaponType() throws DAOException {
//        List<Property> data_list = new LinkedList<Property>();
//        ResultSet set = null;
//        PreparedStatement prestat = null;
//        try {
//            prestat = connection.prepareStatement(getQueryForAllPropertyIncludeInWeaponType());
//            set = prestat.executeQuery();
//            while (set.next()) {
//                data_list.add(new Property(set.getInt(propetyColumnName[0]),
//                        set.getInt(propetyColumnName[1]),
//                        set.getInt(propetyColumnName[2]),
//                        set.getString(propetyColumnName[3]),
//                        set.getInt(propetyColumnName[4]),
//                        set.getString(propertyTypeColumnName[1]),
//                        set.getString(propertyGroupsColumnName[1])));
//            }
//        } catch (SQLException ex) {
//            logger.debug("DAO_propeties", ex);
//            throw new DAOException("Ошибка получения списка данных.", ex);
//        } finally {
//            if (prestat != null) {
//                try {
//                    prestat.close();
//                } catch (SQLException ex) {
//                    logger.debug("DAO_propety", ex);
//                    throw new DAOException("Ошибка закрытия ResultSet`а.", ex);
//                }
//            }
//        }
//        return data_list;
//    }
}
